"""
This python script creates a *Makefile* for issuing commands and executing tasks. This is used to create an interface for running and managing Spyrim-based analysis scripts.

The command is slightly different from Linux to Windows Operating Systems.

	:Linux: make
	:Windows: metamake

Because the script generates a Makefile, ``make`` or ``metamake`` (for Windows) are the only commands needed to start using the defined tasks. However, the commands above **MUST** be issued in the root directory of the project.

.. topic:: A useful command
	
	To see a list of available commands, type ``make ls`` or ``metamake ls``.

.. note::
	This script uses a python module called **metamake** to create and issue tasks.

Available Commands
-------------------

"""
import os, warnings, sys, time, subprocess

#adds the /src/model directory to the PYTHONPATH env var
#this adds support for all the models
models_dir = sys.path[0] + '/src/models/'
if((models_dir in sys.path) == False):
	sys.path.append(models_dir)

#adds the /dependencies/python/Lib/site-packages directory to the PYTHONPATH env var
#this adds support for all the models
depend_dir = sys.path[0] + '/libs/python/'
if((depend_dir in sys.path) == False):
	sys.path.append(depend_dir)

from metamake import task, bootstrap, namespace, Flag
from {{ module.lower() }} import {{ module }}
from {{ module.lower() }}.shell import *
from {{ module.lower() }}.utilities import new_session, are_you_sure, yes_or_no
from {{ module.lower() }}.validations import *
from config_model import *

warnings.filterwarnings("ignore")
bootstrap("Makefile")

root_user = {{ module }}.get_root_user()

def get_param(param_str,given_kwargs):
	"""gets a parameter from the command line"""
	if given_kwargs == None:
		given_kwargs = {}
	if param_str in given_kwargs:
		return given_kwargs[param_str]
	elif analysis_profile.given and param_str == "analysis_profile":
		return analysis_profile.value
	else:
		return None

def prompt_for_param(param_str, prompt_str):
	"""prompts user for a parameter, could also handle validation for given input"""
	param = raw_input(prompt_str)
	if param_str == "analysis_profile":
		validate_config(param,None)
	return param

def get_or_prompt_for_param(param_str, prompt_str, given_kwargs):
	"""returns argument if given or prompts for it if not"""
	param = get_param(param_str,given_kwargs)
	if param:
		return param
	else:
		return prompt_for_param(param_str, prompt_str)

#args
verbosity = Flag("v").explain("sets the output level (a higher value means more verbose), accepted values are 1-4", convert=int, allow=[1,2,3,4])
analysis_profile = Flag("analysis_profile").explain("The name of the analysis effort, such as 'Apr2011' or 'FlightTest2011'. This should be unique to each analysis configuration. An analysis profile defines where the data is, as well as where any results should be output, among other configuration settings.")
load_step = Flag("load_step").explain("This flag allows the user to run only part of the load process. The given argument should be the name of the task to start with.")
# database = Flag("database").explain("sets the database to perform operations on")

#if args given, print values
if verbosity.given:
	print "verbosity param: " + str(verbosity.value)
if analysis_profile.given:
	print "analysis_profile param: " + str(analysis_profile.value)
# if database.given:
	# print "database param: " + str(database.value)

if analysis_profile.given:
	validate_config(analysis_profile.value)

@namespace
def database():
	"""
Contains tasks for starting, stopping and restarting a database server.

.. function:: database.start()

	Used to start a database server.

	:Linux: ``make database:start``
	:Windows: ``metamake database:start``

.. function:: database.stop()

	Used to stop a database server.
	
	:Linux: ``make database:stop``
	:Windows: ``metamake database:stop``

.. function:: database.restart()

	Used to restart a database server.
	
	:Linux: ``make database:restart``
	:Windows: ``metamake database:restart``

	"""
	@task
	def start():
		"""starts the database"""
		if {{ module }}.get_os() == "Linux":
			# shell("sudo /etc/init.d/mysqld start")
			raise NotImplementedError, "Task not yet developed.."
		elif {{ module }}.get_os() == "Windows":
			# shell("net start MySQL")
			raise NotImplementedError, "Task not yet developed.."
		else:
			raise NotImplementedError, "OS not developed for database operations. Task not yet developed.."

	@task
	def stop():
		"""stops the database server"""
		if {{ module }}.get_os() == "Linux":
			# shell("sudo /etc/init.d/mysqld stop")
			raise NotImplementedError, "Task not yet developed.."
		elif {{ module }}.get_os() == "Windows":
			# shell("net stop MySQL")
			raise NotImplementedError, "Task not yet developed.."
		else:
			raise NotImplementedError, "OS not developed for database operations. Task not yet developed.."

	@task
	def restart():
		"""restarts the database server"""
		if {{ module }}.get_os() == "Linux":
			# shell("sudo /etc/init.d/mysqld restart")
			raise NotImplementedError, "Task not yet developed.."
		elif {{ module }}.get_os() == "Windows":
			# shell("net stop MySQL")
			# shell("net start MySQL")
			raise NotImplementedError, "Task not yet developed.."
		else:
			raise NotImplementedError, "OS not developed for database operations. Task not yet developed.."

@task
def mount_data():
	"""mounts a data source. meant for -nix OS' """
	if {{ module }}.get_os() == "Linux":
		raise NotImplementedError, "Task not yet developed.."
		#if not os.path.isdir("/mnt/data"):
			#shell("sudo mkdir /mnt/data")
		#shell("sudo mount -t cifs	-o username=some_user //192.168.2.12/shared/data /mnt/data")
	else:
		raise NotImplementedError, "Operation only supported in -nux based OS'. Task not yet developed.."

@task(default=True)
def default():
	"""This is the default task. Otherwise, it does nothing. It's only a placeholder for if the user enter ``make`` or ``metamake`` without a task."""
	print "No task given. Type 'make ls' or 'metamake ls' for a list of tasks."

@task
def test_connection():
	"""Tests database connectivity using the guest account, which is setup in the config directory.

	:Linux: ``make test_connection``
	:Windows: ``metamake test_connection``

	.. note::
		If the connection fails, try the `config <./makefile.html#Makefile.conf>`_ task to examine the current configuration.

	"""
	## creating connection / session
	# print "\n Connecting to database..."
	
	# try:
		# session = new_session({{ module }}.login('guest')+'/some_database', False)
		# print "\n Connection Succeeded. Database is operational."
		# session.close()
	# except:
		# print "\n Connection Failed. Database is not operational."
	raise NotImplementedError, "Task not yet developed..."

@task
def procedure():
	"""This prints the entire analysis procedure / process. This will print the procedure to the screen.

	:Linux: ``make procedure``
	:Windows: ``metamake procedure``

	"""
	if os.path.exists({{ module }}.get_project_root()+"/doc/procedure.txt"):
		with open({{ module }}.get_project_root()+"/doc/procedure.txt", 'r') as f:
			for line in f:
				print line.strip('\n')
	else:
		raise Exception, "No procedure outlined in %s." % os.path.normpath({{ module }}.get_project_root()+"/doc/procedure.txt")

@task
def conf():
	"""This prints the current configuration.

	:Linux: ``make conf``
	:Windows: ``metamake conf``

	.. note::

		Current Configuration:

		**Server IP**
			This will show you where you're currently pointing the project to. This IP should be the database IP.
		**Current Analysis Event**
			This will show you which test you are currently setup to analyze. 
		**Operating System**
			Shows you the detected operating system. Many functions are OS dependent due to the differences in certain implementatsions between operating systems.
		**Data Directory Root**
			This should be the root directory for the current test.
		**Known drivers**
			This is a list of database drivers that can be used.
	"""
	shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/utilities/current_configuration.py')])
	print


@namespace
def config():
	"""
This namespace facilitates the viewing and modifying current configuration parameters.

..function:: config.new()

	Creates a new configuration

	:Linux: ``make config:new``
	:Windows: ``metamake config:new``

..function:: config.new_profile()

	Creates a new analysis profile

	:Linux: ``make config:new_profile``
	:Windows: ``metamake config:new_profile``

..function:: config.new_driver()

	Creates a new database driver with alias

	:Linux: ``make config:new_driver``
	:Windows: ``metamake config:new_driver``

.. function:: config.drivers()

	Lists current known drivers in configuration db.

	:Linux: ``make config:drivers``
	:Windows: ``metamake config:drivers``

.. function:: config.change_host()

	This prompts the user to change the current ip of the database.

	:Linux: ``make config:change_host``
	:Windows: ``metamake config:change_host``

.. function:: config.change_analysis_profile()

	This prompts the user to change the current test event.

	:Linux: ``make config:change_analysis_profile``
	:Windows: ``metamake config:change_analysis_profile``

.. function:: config.users()

	This prints all the users in the config file.

	:Linux: ``make config:users``
	:Windows: ``metamake config:users``

	"""
	@task
	def new():
		"""create a new configuration"""
		session = {{ module }}.get_config_session()
		
		aes = session.query(AnalysisProfile).all()
		selected_ae = None
		
		if len(aes) == 0:
			selected_ae = config()("new_profile")()
		else:
			
			while(True):
				
				print "\n Please select the host and data dir for this configuration: "
				print " Or, simply press ENTER to create a new data analysis profile.\n"
				
				for ae in aes:
					print "  %i). %s (%s)" % (ae.id, ae.host, ae.data_directory)
				
				choice = raw_input("\n Enter choice: ")
				choice = choice.strip()
				
				if len(choice) != 0:
					selected_ae = session.query(AnalysisProfile).filter(AnalysisProfile.id == choice).all()
					
					if selected_ae:
						selected_ae = selected_ae[0]
						break
					else:
						continue
					
				else:
					selected_ae = config()("new_profile")()

					if selected_ae:
						break
					else:
						continue

			print
			print " %s %s" % ("Host:".ljust(25), selected_ae.host)
			print " %s %s" % ("Data directory:".ljust(25), selected_ae.data_directory)
			print " %s %s" % ("Results directory:".ljust(25), selected_ae.results_directory)
			print " %s %s" % ("Html directory:".ljust(25), selected_ae.html_directory)
			
		# """
		# look at existing analysis events
		# 	if none exists create one
		# 	otherwise ask if wants to use an existing one
		# 	if not create a new one
			
		# ask for a driver
		# 	if want to use an existing one, continue
		# 	otherwise create one
		drvs = session.query(Driver).all()
		selected_drv = None

		while(True):
			
			print "\n Please select the database driver for this configuration: "
			print " Or, simply press ENTER to create a driver.\n"
			
			for drv in drvs:
				print "  %i). %s (%s)" % (drv.id, drv.driver, drv.alias)
			
			choice = raw_input("\n Enter choice: ")
			choice = choice.strip()
			
			if len(choice) != 0:
				selected_drv = session.query(Driver).filter(Driver.id == choice).all()
				
				if selected_drv:
					selected_drv = selected_drv[0]
					break
				else:
					continue
			else:
				selected_drv = config()("new_driver")()

				if selected_drv:
					break
				else:
					continue

		print
		print " %s %s" % ("Alias:".ljust(12), selected_drv.alias)
		print " %s %s" % ("Driver:".ljust(12), selected_drv.driver)

		while(True):
			
			conf_name = raw_input("\n Enter the name for this configuration: ")
			
			if len(conf_name.strip()) == 0:
				print "\n  The configuration name cannot be blank!"
				continue
			else:
				conf = Config(conf_name, selected_ae.id, selected_drv.id)
				
				if len(session.query(Config).filter(Config.conf_name == conf_name).all()) > 0:
					print "Configuration names must be unique!"
					continue
				
				session.add(conf)
				session.commit()
				break
		
		session.close()

	@task
	def new_profile():
		"""creates a new analysis profile"""
		from config_model import AnalysisProfile
		import os
		print "Analysis Profile:\n\n"
		host = raw_input("Database Host (leave blank if no database is being used): ")
		data = raw_input("Data directory (top level data directory for this analysis event): ")

		def_results = os.path.normpath(data+"/results")
		results = raw_input("Results directory [%s]: "%def_results)
		if len(results.strip()) == 0:
			results = def_results

		def_html = os.path.normpath(data+"/results/html")
		html = raw_input("HTML directory [%s]: "%def_html)
		if len(html.strip()) == 0:
			html = def_html
		
		ap = AnalysisProfile(host, data, html, results)
		
		print "\nSummary\n-------\n"
		print "Host: %s" % ap.host
		print "Data Directory: %s" % ap.data_directory
		print "Results Directory: %s" % ap.results_directory
		print "HTML Directory: %s" % ap.html_directory
		
		if yes_or_no("\nDoes this look correct [y]? "):
			session = {{ module }}.get_config_session()
			session.add(ap)
			session.commit()
			session.close()
		
			return ap
		
		return None

	@task
	def new_driver():
		"""creates an alias for a database driver"""
		from config_model import Driver
		import os
		print "Driver:\n\n"
		drvr = raw_input("Database Driver (ie. sqlite:/// or mysql+mysqldb://): ")
		alias = raw_input("Driver Alias: (ie. python_mysql): ")
		
		drv = Driver(drvr, alias)
		
		print "\nSummary\n-------\n"
		print "Driver: %s" % drv.driver
		print "Data Directory: %s" % drv.database
		
		if yes_or_no("\nDoes this look correct [y]? "):
			session = {{ module }}.get_config_session()
			session.add(drv)
			session.commit()
			session.close()
			return drv
			
		return None
		
	@task
	def drivers():
		"""lists current known drivers in config/drivers.csv"""
		print "\n  Defined database drivers\n"
		print "  %s: %s" % ("driver".ljust(20), "alias")
		session = {{ module }}.get_config_session()
		
		from config_model import Driver
		drivers = session.query(Driver).all()
		
		for driver in drivers:
			print "  %s: %s" % (driver.driver.ljust(20), driver.alias)
		session.close()

	@task
	def change_host():
		"""This prompts the user to change the current ip of the database"""
		#shell("python "+{{ module }}.get_project_root()+"/src/utilities/change_host.py")
		host = prompt_for_param("host","\n Please enter new database ip (xxx.xxx.xxx.xxx, or localhost): ")
		from {{ module.lower() }}.utilities import change_host
		change_host(host)

	@task
	def change_analysis_profile(**kwargs):
		"""This prompts the user to change the current test event"""
		print 
		#shell("python "+{{ module }}.get_project_root()+"/src/utilities/change_analysis_profile.py")
		analysis_profile_name = get_or_prompt_for_param("analysis_profile","\n Please enter test name to use: ",kwargs)
		from {{ module.lower() }}.utilities import change_analysis_profile
		change_analysis_profile(analysis_profile_name)

	@task
	def users():
		"""This prints all the users in the config file"""
		
		#prints known login credentials
		print "\n Database Login Info and Drivers:"
		for (k, v,) in {{ module }}.login_info().items():
			print "	 " + str(k) + ":"
			for (k1, v1) in v.items():
				print "		"+str(k1)+":\t"+str(v1)

	def return_func(func_name):
		if func_name == "new": return new
		elif func_name == "new_profile": return new_profile
		elif func_name == "new_driver": return new_driver
	return return_func

@namespace
def admin():
	"""
	This namespace provide administrative actions.

	.. function:: admin.setup()

		Sets up all the databases, tables and views for loading and analysing data.

		:Linux: ``make admin:setup``
		:Windows: ``metamake admin:setup``

	.. function:: admin.setup_users()

		Creates all the database users (except root) necessary for database operations.

		:Linux: ``make admin:setup_users``
		:Windows: ``metamake admin:setup_users``

	.. function:: admin.setup_config()

		Creates SQLite config database

		:Linux: ``make admin:setup_config``
		:Windows: ``metamake admin:setup_config``

	.. function:: admin.clean_project()

		This removes all the unwanted files (.pyc, .pyo, .sql~) from the project dir.

		:Linux: ``make admin:clean_project``
		:Windows: ``metamake admin:clean_project``

	.. function:: admin.remove_data_from()

		Removes all data for the given database(s) and given test_date(s).

		:Linux: ``make admin:remove_data_from``
		:Windows: ``metamake admin:remove_data_from``

	.. function:: admin.drop_table()

		Removes one table in a database.

		:Linux: ``make admin:drop_table``
		:Windows: ``metamake admin:drop_table``

	.. function:: admin.drop_tables_in()

		Removes all tables in a database.

		:Linux: ``make admin:drop_tables_in``
		:Windows: ``metamake admin:drop_tables_in``

	.. function:: admin.drop_views_in()

		Removes all views in a database.

		:Linux: ``make admin:drop_views_in``
		:Windows: ``metamake admin:drop_views_in``

	.. function:: admin.drop_database()

		Drops a given database.

		:Linux: ``make admin:drop_database``
		:Windows: ``metamake admin:drop_database``

	.. function:: admin.generate_blowfish()

		This generates a new blowfish cipher.

		:Linux: ``make admin:generate_blowfish``
		:Windows: ``metamake admin:generate_blowfish``

	.. function:: admin.remove_analysis_profile()

		Removes an entire test event from the database.

		:Linux: ``make admin:remove_analysis_profile``
		:Windows: ``metamake admin:remove_analysis_profile``

	"""
	@task
	def setup():
		"""Sets up all the databases, tables and views for loading and analysing data"""
		# validate_database_running()

		print "\n WARNING!!! This is a very dangerous and destructive task.\n"

		print " Current configuration:"
		print "	 Host: "+{{ module }}.get_current_host()
		print "	 User: "+{{ module }}.get_user(root_user)

		print "\n This should ONLY be used to initialize a new database server!\n"

		if not are_you_sure():
			print "\n Setup canceled..."
			exit(2)
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/setup/setup_db.py')])

	@task
	def setup_users():
		"""Creates all the mysql users (except root) necessary for database operations"""
		# validate_database_running()
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/setup/create_users.py')])

	@task
	def setup_config():
		"""Creates SQLite config database"""
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/setup/setup_config_db.py')])

	@task
	def clean_project():
		"""This removes all the unwanted files (.pyc, .pyo, .sql~) from the project dir"""

		import {{ module.lower() }}.utilities
		{{ module.lower() }}.utilities.remove_unwanted_files({{ module }}.get_project_root(), '*.pyc')
		{{ module.lower() }}.utilities.remove_unwanted_files({{ module }}.get_project_root(), '*.pyo')
		{{ module.lower() }}.utilities.remove_unwanted_files({{ module }}.get_project_root(), '*.sql~')
		{{ module.lower() }}.utilities.remove_unwanted_files({{ module }}.get_project_root(), '*.py~')

	@task
	def drop_table():
		"""removes one table in a database"""
		# validate_database_running()

		# db = get_or_prompt_for_param("database","\n Please enter the database that contains the table to be dropped: ",None)
		# table = prompt_for_param("table","\n Please enter table to be dropped: ")

		# #connection / session
		# print "\n Connecting to database..."
		# session = new_session({{ module }}.login(root_user), False)

		# try:
		# 	session.execute("drop table `"+db+"`.`"+table+"`;")
		# except:
		# 	print sys.exc_info()
		# session.commit()
		# session.close()

		raise NotImplementedError, "Task not yet developed..."


	@task
	def drop_database():
		"""drops a given database"""
		# validate_database_running()

		# print "\n WARNING!!! This is a very dangerous and destructive task.\n"

		# from {{ module.lower() }}.utilities import are_you_sure
		# if not are_you_sure():
		# 	print "\n Task canceled..."
		# 	exit(2)

		# db = get_or_prompt_for_param("database","\n Please enter the database to be cleared: ",None)
		
		# #connection / session
		# print "\n Connecting to database..."
		# session = new_session({{ module }}.login(root_user), False)
		# session.execute("drop database if exists `"+db+"`;")
		# session.close()
		raise NotImplementedError, "Task not yet developed..."

	@task
	def generate_blowfish():
		"""This generates a new blowfish cipher"""
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/utilities/generate_blowfish.py')])

	@task
	def remove_dataset():
		"""removes an entire dataset from the database"""
		# validate_database_running()

		# print "\n WARNING!!! This is a very dangerous and destructive task.\n"

		# from {{ module.lower() }}.utilities import are_you_sure
		# if not are_you_sure():
			# print "\n Task canceled..."
			# exit(2)

		# analysis_profile = get_or_prompt_for_param("analysis_profile","\n Please enter the analysis_profile to be cleared: ", None)
		# some database magic ...
		raise NotImplementedError, "Task not yet developed..."

@namespace
def load():
	"""
	
	This namespace exists to load data into a database.

	.. function:: load.add_data()
		
		This will run all scripts to fully process the new data.

		:Linux: ``make load:add_day test_date=YYYYMMDD``
		:Windows: ``metamake load:add_day test_date=YYYYMMDD``

		.. note::

			This is the order that commands are run in.

			1. `load:some_file <./makefile.html#Makefile.load.some_file>`_
			2. `load:some_other_file <./makefile.html#Makefile.load.some_other_file>`_
			3. `html:setup <./makefile.html#Makefile.html.setup>`_
			4. `results:summary <./makefile.html#Makefile.results.summary>`_
			5. `results:stats <./makefile.html#Makefile.results.stats>`_
			6. `plots:test_plot <./makefile.html#Makefile.plots.test_plot>`_
			7. `html:index <./makefile.html#Makefile.html.index>`_
			8. `html:build <./makefile.html#Makefile.html.build>`_

	.. function:: load.some_file()
		
		This loads all the raw data in data directory.

		:Linux: ``make load:faad_raw test_date=YYYYMMDD``
		:Windows: ``metamake load:faad_raw test_date=YYYYMMDD``

	.. function:: load.some_other_file()

		:Linux: ``make load:some_other_file test_date=YYYYMMDD``
		:Windows: ``metamake load:some_other_file test_date=YYYYMMDD``

	"""
	@task
	def add_data(**kwargs):
		"""This will load and analyze an entire dataset. This task is meant to run the entire process."""
		# validate_database_running()
		# validate_data_directory()

		# date = get_param("test_date", kwargs)
		# if not date: date = prompt_for_param("test_date","\n Please enter a test_date for add_day: ")

		def parse_out_func_name(func):
			import re
			return re.search(".*function (.*) at.*", str(func)).group(1)

		failed = None

		load_step = get_param("load_step", kwargs)

		steps = []
		steps.append([load,"some_file"])
		steps.append([load,"some_other_file"])
		steps.append([html,"setup"])
		steps.append([finished,"summary"])
		steps.append([finished,"class_stats"])
		steps.append([finished,"stats"])
		steps.append([plots,"test_plot"])
		steps.append([html,"index"])
		steps.append([html,"build"])

		# if you only want to run part of the process
		if load_step:
			step_count = 0

			for i, step in enumerate(steps):
				step_name = step[0].__name__+":"+str(step[1])
				if step_name == load_step:
					steps = steps[step_count:]
					break
				step_count += 1

		#this runs all the steps
		t = [time.time()]
		for namespace, task in steps:
			if not failed:
				try:
					namespace()(task)(test_date=date) #looks gross, but it works
				except:
					failed = task
					print sys.exc_info()
				t.append(time.time())

		# prints a summary
		print "Timing summary for "+str(date)+":"
		for i in range(0,len(t)-1): #range(0,n) == [0,1,2,...,n-1]
			print "%.3f"%(t[i+1]-t[i]), "sec for", parse_out_func_name(steps[i][0])+":"+str(steps[i][1])
		if failed:	print "FAILED at step '"+failed+"'"
		print "TOTAL:","%.3f"%(t[len(t)-1]-t[0]),"sec"

		for i in range(0,len(dates)):
			print "%.3f"%(t[i+1]-t[i]), "sec for", dates[i]
		print "TOTAL:","%.3f"%(t[len(dates)]-t[0]),"sec"

	@task
	def some_file(**kwargs):
		"""This loads some of the data in data directory"""
		# validate_database_running()
		# validate_data_directory()

		# date = get_param("date", kwargs)

		print " Loading raw logs..."
		# if date:
			# shell("python "+{{ module }}.get_project_root()+"/src/parse/some_file_type/load_raw.py --test_date " + str(date))
		# else:
			# shell("python "+{{ module }}.get_project_root()+"/src/parse/some_file_type/load_raw.py")
		raise NotImplementedError, "Task not yet developed..."

	@task
	def some_other_file(**kwargs):
		"""This moves all the raw data into its appropriate tables"""
		# validate_database_running()

		print "Parsing some other file..."
		# shell("python "+{{ module }}.get_project_root()+"/src/parse/some_other_file_type/parse_some_file.py")
		raise NotImplementedError, "Task not yet developed..."

	def return_func(func_name):
		if func_name == "add_data": return add_data
		elif func_name == "some_file": return some_file
		elif func_name == "some_other_file": return some_other_file
	return return_func

@namespace
def results():
	"""

	This namespace generates results from the data analysis process.

	.. function:: results.summary()
		
		Generates a summary for the analysis event.

		:Linux: ``make results:summary``
		:Windows: ``metamake results:summary``

	.. function:: results.stats()
		
		Outputs a stats rollup csv file

		:Linux: ``make results:stats``
		:Windows: ``metamake results:stats``

	"""		
	@task
	def summary(**kwargs):
		"""Generates data summary"""
		# validate_database_running()

		# print "\n Generating summary output..."
		# shell('python "%s"' %({{ module }}.get_project_root()+"/src/stats/summary.py"))
		raise NotImplementedError, "Task not yet developed..."

	@task
	def stats(**kwargs):
		"""Outputs a stats rollup"""
		# validate_database_running()
		# validate_data_directory()

		# print "\n Outputting rollup csv files..."
		# shell('python "%s"'  % ({{ module }}.get_project_root()+"/src/stats/finished_stats.py -t "+str(date)))
		raise NotImplementedError, "Task not yet developed..."

	def return_func(func_name):
		if func_name == "summary": return summary
		elif func_name == "stats": return stats
	return return_func


@namespace
def plots():
	"""
		This namespace is used for creating plots.

	.. function:: plots.test_plot()

		This would output a plot, if developed.
		
		:Linux: make plots:test_plot
		:Windows: make plots:test_plot

	"""
	@task
	def test_plot(**kwargs):
		"""This would output a plot, if developed."""
		# validate_database_running()
		# shell('python "%s"' % ({{ module }}.get_project_root()+"/src/plots/python/test_plot.py"))
		raise NotImplementedError, "Task not yet developed..."

	def return_func(func_name):
		if func_name == "test_plot": return test_plot
	return return_func

@namespace
def html():
	"""
	This namespace is meant to hold all related functions used in generating html result summary.

	.. function:: setup()

		This creates the base directory structure for the html generation.

		:Linux: make html:setup
		:Windows: metamake html:setup

	.. function:: index()

		This creates the Table of Contents for the entire html output.

		:Linux: make html:index
		:Windows: metamake html:index

	.. function:: build()

		This builds the html output.

		:Linux: make html:build
		:Windows: metamake html:build

	.. function:: create()

		This task runs all the scripts needed to generate the rst files.

		:Linux: make html:create
		:Windows: metamake html:create

	.. function:: run()

		This task runs create, index and build in one step.

		:Linux: make html:run
		:Windows: metamake html:run

	.. function:: update()

		This task runs index and build in one step.

		:Linux: make html:update
		:Windows: metamake html:update

	"""
	@task
	def setup(**kwargs):
		"""This creates the boiler plate directory structure for using sphinx."""
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/html/setup.py')])

	@task
	def index(**kwargs):
		"""This creates the Table of Contents for the entire html output."""
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/html/index.py')])

	@task
	def build(**kwargs):
		"""This builds the html output."""
		shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/html/build.py')])

	@task
	def create(**kwargs):
		"""This task runs all the scripts which generate the RST files."""
		# shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/html/stats/summary.py')])
		# shell_array(["python",  '%s' % os.path.normpath({{ module }}.get_project_root()+'/src/html/stats/indepth_results.py')])
		raise NotImplementedError, "This task is not yet developed..."

	@task
	def run(**kwargs):
		"""This task runs all the scripts which create the rst docs, index and build in one step."""
		# validate_database_running()
		# validate_data_directory()
		
		failed = None

		def parse_out_func_name(func):
			import re
			return re.search(".*function (.*) at.*", str(func)).group(1)

		steps = []
		steps.append([html,"create"])
		steps.append([html,"index"])
		steps.append([html,"build"])
		t = [time.time()]
		for namespace, task in steps:
			if not failed:
				try:
					namespace()(task)()
				except:
					failed = task
				t.append(time.time())

		print "\nTiming summary:"
		for i in range(0,len(t)-1): #range(0,n) == [0,1,2,...,n-1]
			print "%.3f"%(t[i+1]-t[i]), "sec for", parse_out_func_name(steps[i][0])+":"+str(steps[i][1])
		if failed:	print "FAILED at step '"+failed+"'"
		print "TOTAL:","%.3f"%(t[len(t)-1]-t[0]),"sec"

	@task
	def update(**kwargs):
		"""This task updates the indexes and builds the latest html."""
		# validate_database_running()
		# validate_data_directory()
		steps = []
		t = [time.time()]

		# indexing html (generating index.rst files - A.K.A. Table of Contents)
		steps.append("index")
		index()
		t.append(time.time())

		# building html files
		steps.append("build")
		build()
		t.append(time.time())
		
		print "Timing summary for html:update :"
		for i in range(0,len(steps)):
			print "%.3f"%(t[i+1]-t[i]), "sec for", (steps)[i]
		print "TOTAL:","%.3f"%(t[len(steps)]-t[0]),"sec"

	def return_func(func_name):
		if func_name == "setup": return setup
		elif func_name == "index": return index
		elif func_name == "build": return build
		elif func_name == "run": return run
		elif func_name == "create": return create
		elif func_name == "update": return update
	return return_func
